<?php

abstract class FP_Model_Gateway_Doctrine_Adapter extends FP_Model_Gateway {

    public function create(array $pData=array(), $pAutoSave=false, $pAutoSaveValidate=true){
	$model = new $this->_model_class($pData, $this);
	if ($pAutoSave)
	    $model->save($pAutoSaveValidate);
	return $model;
    }

    public function save(array $pData){
	$pData = $this->preSave($pData);

	if (!is_null($pData[$this->_primary_key])){
	    $_record = Doctrine_Query::create()
	    ->from("{$this->_dao_class} o")
	    ->where("o.{$this->_primary_key} = ?", $pData[$this->_primary_key])
	    ->fetchOne();
	} else
	    $_record = new $this->_dao_class();

	if ($_record){
	    $_record->fromArray($pData);
	    $_record->save();
	} else
	    throw new Exception('Invalid record id');

	$_data = $_record->toArray();
	$_data = $this->postSave($_data);

	return $_data;
    }

    public function delete($pModel){
	$object = $this->getModelObject($pModel);
	$object = $this->preDelete($object);

	$result = Doctrine_Query::create()
	    ->delete("{$this->_dao_class} o")
	    ->where("o.{$this->_primary_key} = ?", $object->__get($this->_primary_key))
	    ->execute();

	$this->postDelete($object->__get($this->_primary_key));

	return $result;
    }

    public function count($pCriteria=null){
	if (is_array($pCriteria)){
	    $q = Doctrine_Query::create()
	    ->select("COUNT(o.{$this->_primary_key})")
	    ->from("{$this->_dao_class} o");

	    $q = $this->buildQuery($pCriteria, $q);

	    if (array_key_exists('$debug', $pCriteria) && $pCriteria['$debug'])
		logdebug($q->getSqlQuery() . ' - ' . logobject($q->getParams()));

	    return $q->execute(array(), Doctrine::HYDRATE_SINGLE_SCALAR);
	} else {
	    $q = Doctrine_Query::create()
	    ->select("COUNT(o.{$this->_primary_key})")
	    ->from("{$this->_dao_class} o");
	    return $q->execute(array(), Doctrine::HYDRATE_SINGLE_SCALAR);
	}
    }

    public function fetch($pCriteria=null, $pOffset=null, $pLimit=null, $pOrderBy=null){
	if (is_numeric($pCriteria)){

	    $_record = Doctrine_Query::create()
	    ->from("{$this->_dao_class} o")
	    ->where("o.{$this->_primary_key} = $pCriteria")
	    ->fetchOne();

	    if ($_record)
		return new $this->_model_class($_record->toArray(), $this);
	} else if (is_array($pCriteria)){

	    $q = Doctrine_Query::create()
	    ->from("{$this->_dao_class} o");

	    $q = $this->buildQuery($pCriteria, $q, $pOffset, $pLimit, $pOrderBy);
	    if (array_key_exists('$debug', $pCriteria) && $pCriteria['$debug'])
		logdebug($q->getSqlQuery() . ' - ' . logobject($q->getParams()));

	    $_records = $q->execute();
	    return new $this->_model_collection_class($_records->toArray(), $this, $this->_model_class);
	} else {

	    $q = Doctrine_Query::create()
	    ->from("{$this->_dao_class} o");

	    $q = $this->buildQuery(null, $q, $pOffset, $pLimit, $pOrderBy);

// 	    logdebug($q->getSqlQuery() . ' - ' . logobject($q->getParams()));
	    $_records = $q->execute();
	    return new $this->_model_collection_class($_records->toArray(), $this, $this->_model_class);
	}
    }

    protected function buildQuery($pCriteria, $query, $pOffset=null, $pLimit=null, $pOrderBy=null){
	$joined = array();

	$first = true;
	if ($pCriteria && count($pCriteria) > 0){
	    foreach ($pCriteria as $key => $val){

		if ($key == '$debug'){
		    unset($pCriteria[$key]);
		    break;
		}

		if (preg_match('/^!.*/', $key)){
		    $key = preg_replace('/^!/', '', $key);
		    $negate = true;
		} else
		    $negate = false;

		if ($first){
		    if (preg_match('/^\(.*\)$/', $key)){
			if ($negate)
			    $clause_func = 'whereNotIn';
			else
			    $clause_func = 'whereIn';
		    } else
			$clause_func = 'where';
		} else {
		    if (preg_match('/^\|\|.*/', $key)){
			$clause_func = 'orWhere';
			$key = preg_replace('/^\|\|/', '', $key);
		    } else if (preg_match('/^\(.*\)$/', $key)){
			if ($negate)
			    $clause_func = 'andWhereNotIn';
			else
			    $clause_func = 'andWhereIn';
		    } else
			$clause_func = 'andWhere';
		}
		if (preg_match('/^sql:/', $key)){ //Raw sql where condition

			$sql = $val;

			if (preg_match_all('/([a-zA-Z]\w+\.\w+)/', $val, $matches)){ //for joins

				foreach ($matches[1] as $match){
					$keyparts = explode('.', $match);
					if (!array_key_exists($keyparts[0], $joined)){
					    $alias = "j" . strval(count($joined) + 1);
					    $joined[$keyparts[0]] = $alias;
					    $query = $query->leftJoin("o.{$keyparts[0]} $alias");
					} else
					    $alias = $joined[$keyparts[0]];

					$sql = preg_replace("/{$keyparts[0]}\.{$keyparts[1]}/", "{$alias}.{$keyparts[1]}", $sql);
				}
		    }

		    $query = call_user_func(array($query, $clause_func), $sql);

		} else if (preg_match('/^%.*%$/', $key)){ //LIKE
		    $key = preg_replace('/(^%|%$)/', '', $key);

		    if (preg_match('/\./', $key)){ //for joins
				$keyparts = explode('.', $key);
				if (!array_key_exists($keyparts[0], $joined)){
				    $alias = "j" . strval(count($joined) + 1);
				    $joined[$keyparts[0]] = $alias;
				    $query = $query->leftJoin("o.{$keyparts[0]} $alias");
				} else
				    $alias = $joined[$keyparts[0]];

				$key = "{$alias}.{$keyparts[1]}";
		    }

		    if ($negate)
			$query = call_user_func(array($query,$clause_func), "$key NOT LIKE ?", '%' . $val . '%');
		    else
			$query = call_user_func(array($query,$clause_func), "$key LIKE ?", '%' . $val . '%');

		} else if (preg_match('/^\(.*\)$/', $key)){ //IN
		    $key = preg_replace('/(^\(|\)$)/', '', $key);

		    if (preg_match('/\./', $key)){ //for joins
			$keyparts = explode('.', $key);
			if (!array_key_exists($keyparts[0], $joined)){
			    $alias = "j" . strval(count($joined) + 1);
			    $joined[$keyparts[0]] = $alias;
			    $query = $query->leftJoin("o.{$keyparts[0]} $alias");
			} else
			    $alias = $joined[$keyparts[0]];

			$key = "{$alias}.{$keyparts[1]}";
		    }

		    if (!is_array($val))
			$val = array($val);

		    $query = call_user_func(array($query,$clause_func), $key, $val);
		    /*
		    if ($negate)
			$query = call_user_func(array($query,$clause_func), "$key NOT IN ?", array($val));
		    else
			$query = call_user_func(array($query,$clause_func), "$key IN ?", array($val));
		    */

		} else { //Comparison
		    if (preg_match('/\./', $key)){
			$keyparts = explode('.', $key);
			if (!array_key_exists($keyparts[0], $joined)){
			    $alias = "j" . strval(count($joined) + 1);
			    $joined[$keyparts[0]] = $alias;
			    $query = $query->leftJoin("o.{$keyparts[0]} $alias");
			} else
			    $alias = $joined[$keyparts[0]];

			$key = "{$alias}.{$keyparts[1]}";
		    }

		    if ($negate){
			if (is_null($val))
			    $query = call_user_func(array($query,$clause_func), "$key IS NOT NULL");
			else
			    $query = call_user_func(array($query,$clause_func), "$key != ?", $val);
		    } else {
			if (is_null($val))
			    $query = call_user_func(array($query,$clause_func), "$key IS NULL");
			else
			    $query = call_user_func(array($query,$clause_func), "$key = ?", $val);
		    }
		}

		$first = false;
	    }
	}
	if ($pOffset)
	    $query = $query->offset($pOffset);

	if ($pLimit)
	    $query = $query->limit($pLimit);

	if ($pOrderBy){
	    if (preg_match('/\./', $pOrderBy)){
		if (preg_match('/\s+/', trim($pOrderBy))){
		$parts = explode(' ', $pOrderBy);
		$field_parts = explode('.', $parts[0]);
		} else{
		$parts = null;
		$field_parts = explode('.', $pOrderBy);
		}

		if (!array_key_exists($field_parts[0], $joined)){
		$alias = "j" . strval(count($joined) + 1);
		$joined[$field_parts[0]] = $alias;
		$query = $query->leftJoin("o.{$field_parts[0]} $alias");
		} else
		$alias = $joined[$field_parts[0]];
		$sort = ($parts) ? $parts[1] : '';
		$query = $query->orderBy("{$alias}.{$field_parts[1]} $sort");
	    } else
		$query = $query->orderBy($pOrderBy);
	}

	return $query;
    }

    public function sql($pSql){
	return Doctrine_Manager::getInstance()->getCurrentConnection()->fetchAssoc($pSql);
    }

    public function getTable(){

	return Doctrine_Core::getTable($this->_dao_class);

    }

    public function truncate(){

	$dao = new $this->_dao_class();
	$table_name = $dao->getTable()->getTableName();

	$doctrine = Doctrine_Manager::getInstance()->getCurrentConnection()->getDbh();
	$doctrine->query("TRUNCATE TABLE {$table_name}");
	unset($doctrine);

    }
}

?>